/*
 * Copyright (c) 2025 derreisende77.
 * This code was developed as part of the MediathekView project https://github.com/mediathekview/MediathekView
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package mediathek.tool.ttml.exporters;

import mediathek.tool.ttml.ITtmlExporter;
import mediathek.tool.ttml.Subtitle;
import mediathek.tool.ttml.TimedTextMarkupLanguageParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Convert internal representation into Advanced Substation Alpha Format and save to file.
 *
 * <p>References:
 *
 * <p>- <a href="https://github.com/libass/libass/wiki/ASS-File-Format-Guide">ASS File Format Guide</a>
 *
 * <p>- <a href="https://aegisub.org/docs/latest/styles/#the-style-editor">Aegisub Style Editor</a>
 *
 * <p>- <a href="https://aegisub.org/docs/latest/ass_tags/">Aegisub ASS tags</a>
 */
public class AdvancedSubstationAlphaExporter implements ITtmlExporter {
    private static final Logger logger = LogManager.getLogger();
    private final SimpleDateFormat assFormat = new SimpleDateFormat("HH:mm:ss.SSS");

    /**
     * Trim milliseconds to deciseconds, SimpleDateFormat does not provide this functionality
     */
    private String getAssTime(Date time) {
        final String assTime = assFormat.format(time);
        return assTime.substring(0, assTime.length() - 1);
    }

    /**
     * Convert a {@link Color} into a BGR hex string
     */
    private String colorToBGR(Color color) {
        return String.format("%02X%02X%02X", color.getBlue(), color.getGreen(), color.getRed());
    }

    /**
     * Converts a hex string to a {@link Color}. If it can't be converted null is returned.
     *
     * @param hex (i.e. #CCCCCCFF or CCCCCC)
     * @return Color
     */
    private Color hexToColor(@NotNull String hex) {
        // based on https://stackoverflow.com/a/43764322
        hex = hex.replace("#", "");
        if (hex.length() == 6) {
            return new Color(
                    Integer.valueOf(hex.substring(0, 2), 16),
                    Integer.valueOf(hex.substring(2, 4), 16),
                    Integer.valueOf(hex.substring(4, 6), 16));
        }
        else if (hex.length() == 8) {
            return new Color(
                    Integer.valueOf(hex.substring(0, 2), 16),
                    Integer.valueOf(hex.substring(2, 4), 16),
                    Integer.valueOf(hex.substring(4, 6), 16),
                    Integer.valueOf(hex.substring(6, 8), 16));
        }
        // return a default white color if something failed
        logger.error("Failed to convert hex color string: {}", hex);
        return Color.WHITE;
    }

    @Override
    public void write(TimedTextMarkupLanguageParser parser, Path assFile) {
        try (FileOutputStream fos = new FileOutputStream(assFile.toFile());
             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
             PrintWriter writer = new PrintWriter(osw)) {
            writer.print(
                    """
                            [Script Info]
                            ; Script generated by MediathekView
                            ScriptType: v4.00+
                            ScaledBorderAndShadow: yes
                            YCbCr Matrix: None
                            
                            [V4+ Styles]
                            Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
                            Style: Default,,22,,,,,0,0,0,0,100,100,0,0,3,1,0,2,10,10,25,1
                            
                            [Events]
                            Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
                            """);
            for (Subtitle title : parser.getSubtitleList()) {
                final String titleRegion = title.getRegion();
                final String titleBegin = getAssTime(title.getBegin());
                final String titleEnd = getAssTime(title.getEnd());

                String titleText = "Dialogue: 0," + titleBegin + "," + titleEnd + ",Default,,0,0,0,,";

                if (titleRegion != null) {
                    // \an = alignment
                    // alignment always affects the whole ASS dialogue line
                    titleText += "{\\an" + titleRegion + "}";
                }

                StringBuilder entryText = new StringBuilder();
                for (var entry : title.getListOfStrings()) {
                    // Assume new lines between same title entries, check if there's content already present
                    if (!entryText.isEmpty()) {
                        entryText.append("\\N");
                    }

                    // start formatting options
                    entryText.append("{");

                    final String entryColorString = entry.getColor();
                    if (!entryColorString.isEmpty()) {
                        // \1c = primary fill color
                        entryText.append("\\1c&H")
                                .append(colorToBGR(hexToColor(entryColorString)))
                                .append("&");
                    }

                    final String entryBackgroundColorString = entry.getBackgroundColor();
                    if (!entryBackgroundColorString.isEmpty()) {
                        final Color entryBackgroundColor = hexToColor(entryBackgroundColorString);
                        // \3c = border color
                        entryText.append("\\3c&H")
                                .append(colorToBGR(entryBackgroundColor))
                                .append("&");

                        // \3a = border alpha
                        // The value is inverted to regular RGBA alpha
                        entryText.append("\\3a&H")
                                .append(String.format("%02X", 255 - entryBackgroundColor.getAlpha()))
                                .append("&");
                    }

                    // end formatting options
                    entryText.append("}");

                    // lastly add actual subtitle text
                    // ORF inline line break tags, replace them when found
                    entryText.append(entry.getText().replace("<br/>", "\\N"));
                }
                writer.println(titleText + entryText);
            }
            logger.trace("Export to ASS was succesful.");
        }
        catch (Exception ex) {
            logger.error("File: {}", assFile, ex);
        }
    }
}
